/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 isometric_map.h

	$Header: /heroes4/isometric_map.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( ISOMETRIC_MAP_H_INCLUDED )
#define ISOMETRIC_MAP_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <cassert>
#include <vector>

#include "copy_on_write_ptr.h"
#include "screen_point.h"
#include "map_point.h"

// -------------------------------------------------------------------
// t_isometric_map_row_data struct
// -------------------------------------------------------------------

struct t_isometric_map_row_data
{
	int offset;
	int start;
	int end;
};

// -------------------------------------------------------------------
// t_isometric_map_base_base class
// -------------------------------------------------------------------

struct t_isometric_map_base_base
{
public:
	// Member functions
	t_map_point_2d get_map_point( t_screen_point const & point ) const;
	int            get_row_end( int row ) const;
	int            get_row_size( int row ) const;
	int            get_row_start( int row ) const;
	t_screen_point get_screen_point( t_map_point_2d const & point ) const;
	int            get_size() const;
	int			   get_tile_height() const;
	t_screen_point get_view_size() const; 
	bool           is_valid( t_map_point_2d const & point ) const;

protected:
	// Types
	typedef t_isometric_map_row_data t_row_data;
	
	// Data members
	int													m_size;
	int													m_tile_height;
	int													m_level_size;
	t_screen_point										m_view_size;
	t_copy_on_write_ptr< std::vector< t_row_data > >	m_row_data_ptr;

	// Constructor/Destructor
	t_isometric_map_base_base();
	~t_isometric_map_base_base() {}

	// Member functions
	void base_initialize( int size, int tile_height, t_screen_point const & view_size );
	int get_row_offset( int row ) const;
};

inline int t_isometric_map_base_base::get_row_end( int row ) const
{
	assert( row >= 0 && row < m_size );
	return ( *m_row_data_ptr )[ row ].end;
}

inline int t_isometric_map_base_base::get_row_size( int row ) const
{
	assert( row >= 0 && row < m_size );
	return get_row_end( row ) - get_row_start( row );
}

inline int t_isometric_map_base_base::get_row_start( int row ) const
{
	assert( row >= 0 && row < m_size );
	return ( *m_row_data_ptr )[ row ].start;
}

inline int t_isometric_map_base_base::get_size() const
{
	return m_size;
}

inline int t_isometric_map_base_base::get_tile_height() const
{
	return m_tile_height;
}

inline t_screen_point t_isometric_map_base_base::get_view_size() const
{
	return m_view_size;
}

inline int t_isometric_map_base_base::get_row_offset( int row ) const
{
	assert( row >= 0 && row < m_size );
	return ( *m_row_data_ptr )[ row ].offset;
}

inline bool t_isometric_map_base_base::is_valid( t_map_point_2d const & point ) const
{
	if (point.row < 0 || point.row >= m_size)
		return false;
	return point.column >= get_row_start( point.row ) 
		   && point.column < get_row_end( point.row );
}

// -------------------------------------------------------------------
// t_isometric_tile_map_base class
// -------------------------------------------------------------------
class t_isometric_tile_map_base : public t_isometric_map_base_base
{
protected:
	// Constructor/Destructor
	t_isometric_tile_map_base() {}
	~t_isometric_tile_map_base() {}

	// Member functions
	void initialize( int size, int tile_height );                 // construct from map size
	void initialize( t_screen_point const & view_size, int tile_height ); // construct from view size
	void initialize( int size, int tile_height, t_screen_point const & view_size );
};

inline void t_isometric_tile_map_base::initialize( int size, int tile_height, t_screen_point const & view_size )
{
	base_initialize( size, tile_height, view_size );
}

// -------------------------------------------------------------------
// t_isometric_vertex_map_base class
// -------------------------------------------------------------------
class t_isometric_vertex_map_base : public t_isometric_map_base_base
{
protected:
	// Constructor/Destructor
	t_isometric_vertex_map_base() {}
	~t_isometric_vertex_map_base() {}

	// Member functions
	void initialize( int size, int tile_height );                 // construct from map size
	void initialize( t_screen_point const & view_size, int tile_height ); // construct from view size
	void initialize( int size, int tile_height, t_screen_point const & view_size );
};

// ---------------------------------------------------------------------
// template for isometric map, with type safe access to data.
// ---------------------------------------------------------------------
template < typename t_base, typename t_elem >
class t_basic_isometric_map : public t_base
{
public:
	// Member functions
	t_elem &		get( t_map_point_2d const & point );
	t_elem const &	get( t_map_point_2d const & point ) const;
	t_elem &		get( t_level_map_point_2d const & point );
	t_elem const &	get( t_level_map_point_2d const & point ) const;
	int				get_levels() const;
	void			initialize(
						int				size,
						int				levels,
						int				tile_height,
						t_elem const &	value = t_elem() );
	void			initialize(
						t_screen_point const &	view_size,
						int						tile_height,
						t_elem const &			value = t_elem() );
	void			initialize(
						int						size,
						int						tile_height,
						t_screen_point const &	view_size,
						t_elem const &			value = t_elem() );
	bool			is_valid( t_map_point_2d const & point ) const;
	bool			is_valid( t_level_map_point_2d const & point ) const;

protected:
	// Data members
	int				      m_levels;
	std::vector< t_elem > m_data;

	// Constructors/Destructor
	t_basic_isometric_map();
	t_basic_isometric_map(
		int				size,
		int				levels,
		int				tile_height,
		t_elem const &	value );
	t_basic_isometric_map(
		t_screen_point const &	view_size,
		int						tile_height,
		t_elem const &			value );
	t_basic_isometric_map(
		int						size,
		int						tile_height,
		t_screen_point const &	view_size,
		t_elem const &			value );
	~t_basic_isometric_map() {}
};

template < typename t_base, typename t_elem >
inline t_basic_isometric_map< t_base, t_elem >::t_basic_isometric_map()
	:	m_levels( 0 )
{
}

template < typename t_base, typename t_elem >
inline t_basic_isometric_map< t_base, t_elem >::t_basic_isometric_map(
	int				size,
	int				levels,
	int				tile_height,
	t_elem const &	value )
{
	initialize( size, levels, tile_height, value );
}

template < typename t_base, typename t_elem >
inline t_basic_isometric_map< t_base, t_elem >::t_basic_isometric_map(
	t_screen_point const &	view_size,
	int						tile_height,
	t_elem const &			value ) 
{
	initialize( view_size, tile_height, value );
}

template < typename t_base, typename t_elem >
inline t_basic_isometric_map< t_base, t_elem >::t_basic_isometric_map(
	int						size,
	int						tile_height,
	t_screen_point const &	view_size,
	t_elem const &			value )
{
	initialize( size, tile_height, view_size, value );
}

template < typename t_base, typename t_elem >
inline t_elem & t_basic_isometric_map< t_base, t_elem >::get( t_map_point_2d const & point )
{
	assert( point.row >= 0 && point.row < get_size() && point.column >= get_row_start( point.row )
		    && point.column < get_row_end( point.row ) );
	assert( m_levels == 1 );
	return m_data[ point.column + get_row_offset( point.row )];
}

template < typename t_base, typename t_elem >
inline t_elem const & t_basic_isometric_map< t_base, t_elem >::get( t_map_point_2d const & point ) const
{
	assert( point.row >= 0 && point.row < get_size() && point.column >= get_row_start( point.row )
		    && point.column < get_row_end( point.row ) );
	assert( m_levels == 1 );
	return m_data[ point.column + get_row_offset( point.row ) ];
}

template < typename t_base, typename t_elem >
inline t_elem & t_basic_isometric_map< t_base, t_elem >::get( t_level_map_point_2d const & point )
{
	assert( point.row >= 0 && point.row < get_size() && point.column >= get_row_start( point.row )
		    && point.column < get_row_end( point.row ) );
	assert( point.level >= 0 && point.level < m_levels );
	return m_data[ point.column + get_row_offset( point.row ) + point.level * m_level_size ];
}

template < typename t_base, typename t_elem >
inline t_elem const & t_basic_isometric_map< t_base, t_elem >::get( t_level_map_point_2d const & point ) const
{
	assert( point.row >= 0 && point.row < get_size() && point.column >= get_row_start( point.row )
		    && point.column < get_row_end( point.row ) );
	assert( point.level >= 0 && point.level < m_levels );
	return m_data[ point.column + get_row_offset( point.row ) + point.level * m_level_size ];
}

template < typename t_base, typename t_elem >
inline int t_basic_isometric_map< t_base, t_elem >::get_levels() const
{
	return m_levels;
}

template < typename t_base, typename t_elem >
inline void t_basic_isometric_map< t_base, t_elem >::initialize(
	int				size,
	int				levels,
	int				tile_height,
	t_elem const &	value )
{
	m_levels = levels;
	t_base::initialize( size, tile_height );
	m_data.resize( m_level_size * levels, value );
}

template < typename t_base, typename t_elem >
inline void t_basic_isometric_map< t_base, t_elem >::initialize(
	t_screen_point const &	view_size,
	int						tile_height,
	t_elem const &			value )
{
	m_levels = 1;
	t_base::initialize( view_size, tile_height );
	m_data.resize( m_level_size, value );
}

template < typename t_base, typename t_elem >
inline void t_basic_isometric_map< t_base, t_elem >::initialize(
	int						size,
	int						tile_height,
	t_screen_point const &	view_size,
	t_elem const &			value )
{
	m_levels = 1;
	t_base::initialize( size, tile_height, view_size );
	m_data.resize( m_level_size, value );
}

template < typename t_base, typename t_elem >
inline bool t_basic_isometric_map< t_base, t_elem >::is_valid( t_map_point_2d const & point ) const
{
	return t_base::is_valid( point );
}

template < typename t_base, typename t_elem >
inline bool t_basic_isometric_map< t_base, t_elem >::is_valid( t_level_map_point_2d const & point ) const
{
	return point.level >= 0 && point.level < m_levels 
		   && t_base::is_valid( point );
}

// ---------------------------------------------------------------------
// t_isometric_map class template
// ---------------------------------------------------------------------

template < typename t_elem >
class t_isometric_map
	:	public t_basic_isometric_map< t_isometric_tile_map_base, t_elem >
{
	typedef t_basic_isometric_map< t_isometric_tile_map_base, t_elem > t_base;

public:
	// Constructors
	t_isometric_map() {}
	t_isometric_map(
		int				size,
		int				levels,
		int				tile_height,
		t_elem const &	value = t_elem() );
	t_isometric_map(
		t_screen_point const &	view_size,
		int						tile_height,
		t_elem const &			value = t_elem() );
	t_isometric_map(
		int						size,
		int						tile_height,
		t_screen_point const &	view_size,
		t_elem const &			value = t_elem() );
};

template < typename t_elem >
inline t_isometric_map< t_elem >::t_isometric_map(
	int				size,
	int				level,
	int				tile_height,
	t_elem const &	value )
	:	t_base( size, level, tile_height, value )
{
}

template < typename t_elem >
inline t_isometric_map< t_elem >::t_isometric_map(
	t_screen_point const &	view_size,
	int						tile_height,
	t_elem const &			value )
	:	t_base( view_size, tile_height, value )
{
}

template < typename t_elem >
inline t_isometric_map< t_elem >::t_isometric_map(
	int						size,
	int						tile_height,
	t_screen_point const &	view_size,
	t_elem const &			value )
	:	t_base( size, tile_height, view_size, value )
{
}

// ---------------------------------------------------------------------
// t_isometric_vertex_map class template
// ---------------------------------------------------------------------

template < typename t_elem >
class t_isometric_vertex_map
	:	public t_basic_isometric_map< t_isometric_vertex_map_base, t_elem >
{
	typedef t_basic_isometric_map< t_isometric_vertex_map_base, t_elem > t_base;

public:
	// Constructors
	t_isometric_vertex_map() {}
	t_isometric_vertex_map(
		int				size,
		int				levels,
		int				tile_height,
		t_elem const &	value = t_elem() );
	t_isometric_vertex_map(
		t_screen_point const &	view_size,
		int						tile_height,
		t_elem const &			value = t_elem() );
	t_isometric_vertex_map(
		int						size,
		int						tile_height,
		t_screen_point const &	view_size,
		t_elem const &			value = t_elem() );
};

template < typename t_elem >
inline t_isometric_vertex_map< t_elem >::t_isometric_vertex_map(
	int				size,
	int				level,
	int				tile_height,
	t_elem const &	value )
	:	t_base( size, level, tile_height, value )
{
}

template < typename t_elem >
inline t_isometric_vertex_map< t_elem >::t_isometric_vertex_map(
	t_screen_point const &	view_size,
	int						tile_height,
	t_elem const &			value )
	:	t_base( view_size, tile_height, value )
{
}

template < typename t_elem >
inline t_isometric_vertex_map< t_elem >::t_isometric_vertex_map(
	int						size,
	int						tile_height,
	t_screen_point const &	view_size,
	t_elem const &			value )
	:	t_base( size, tile_height, view_size, value )
{
}

#endif // !defined( ISOMETRIC_MAP_H_INCLUDED )
